﻿using System;

namespace ParserGenerator.Extensions
{
    public static class CharExtensions
    {
        public const int MinChar = 0;
        public const int MaxChar = 0x10FFFF;

        public static bool IsLineTerminator(this char c)
        {
            return IsLineTerminator((int)c);
        }

        public static bool IsLineTerminator(this int c)
        {
            switch (c)
            {
                case 0x000a: // '\n'
                case 0x000b: // '\v'
                case 0x000c: // '\f'
                case 0x000d: // '\r'
                case 0x0085: // '\u0085'
                case 0x2028: // '\u2028'
                case 0x2029: // '\u2029'
                    return true;
                default:
                    return false;
            }
        }

        public static bool IsWhiteSpace(this char c)
        {
            return IsWhiteSpace((int)c);
        }

        public static bool IsWhiteSpace(this int c)
        {
            switch (c)
            {
                case 0x0009: // '\t'
                case 0x000a: // '\n'
                case 0x000b: // '\v'
                case 0x000c: // '\f'
                case 0x000d: // '\r'
                case 0x0020: // ' '
                case 0x0085: // '\u0085'
                case 0x00a0: // '\u00a0'
                case 0x1680: // '\u1680'
                case 0x2000: // '\u2000'
                case 0x2001: // '\u2001'
                case 0x2002: // '\u2002'
                case 0x2003: // '\u2003'
                case 0x2004: // '\u2004'
                case 0x2005: // '\u2005'
                case 0x2006: // '\u2006'
                case 0x2007: // '\u2007'
                case 0x2008: // '\u2008'
                case 0x2009: // '\u2009'
                case 0x200a: // '\u200a'
                case 0x2028: // '\u2028'
                case 0x2029: // '\u2029'
                case 0x202f: // '\u202f'
                case 0x205f: // '\u205f'
                case 0x3000: // '\u3000'
                    return true;
                default:
                    return false;
            }
        }

        public static bool IsNonLineTerminatorWhiteSpace(this char c)
        {
            return IsNonLineTerminatorWhiteSpace((int)c);
        }

        public static bool IsNonLineTerminatorWhiteSpace(this int c)
        {
            switch (c)
            {
                case 0x0009: // '\t'
                case 0x0020: // ' '
                case 0x00a0: // '\u00a0'
                case 0x1680: // '\u1680'
                case 0x2000: // '\u2000'
                case 0x2001: // '\u2001'
                case 0x2002: // '\u2002'
                case 0x2003: // '\u2003'
                case 0x2004: // '\u2004'
                case 0x2005: // '\u2005'
                case 0x2006: // '\u2006'
                case 0x2007: // '\u2007'
                case 0x2008: // '\u2008'
                case 0x2009: // '\u2009'
                case 0x200a: // '\u200a'
                case 0x202f: // '\u202f'
                case 0x205f: // '\u205f'
                case 0x3000: // '\u3000'
                    return true;
                default:
                    return false;
            }
        }

        public static int ValueOfDigit(this char c)
        {
            return ValueOfDigit((int)c);
        }

        public static int ValueOfDigit(this int c)
        {
            if ('0' <= c && c <= '9')
                return c - '0';
            else if ('a' <= c && c <= 'f')
                return c - 'a' + 10;
            else if ('A' <= c && c <= 'F')
                return c - 'A' + 10;
            else
                return -1;
        }

        public static bool IsQuoteDelimiter(this char ch)
        {
            switch (ch)
            {
                case '\'':
                case '"':
                case '`':
                    return true;
                default:
                    return false;
            }
        }

        public static string Escape(this char c, char delimiter)
        {
            if (!delimiter.IsQuoteDelimiter())
                throw new ArgumentException(SR.DelimiterMustBeQuote, nameof(delimiter));
            return Escape((int)c, delimiter);
        }

        public static string Escape(this int c, char delimiter)
        {
            if (c < MinChar || c > MaxChar)
                throw new ArgumentOutOfRangeException(nameof(c), SR.InvalidUnicodeCodepoint);
            if (!delimiter.IsQuoteDelimiter())
                throw new ArgumentException(SR.DelimiterMustBeQuote, nameof(delimiter));
            switch (c)
            {
                case '\0': return "\\0";
                case '\a': return "\\a";
                case '\b': return "\\b";
                case '\t': return "\\t";
                case '\n': return "\\n";
                case '\v': return "\\v";
                case '\f': return "\\f";
                case '\r': return "\\r";
                case '\x1b': return "\\e";
                case '\\': return "\\\\";
                case '\'': return delimiter == '\'' ? "\\'" : "'";
                case '"': return delimiter == '"' ? "\\\"" : "\"";
                case '`': return delimiter == '`' ? "\\`" : "`";
                default:
                    if (' ' <= c && c <= 0x7E)
                        return "" + (char)c;
                    else if (1 <= c && c <= 26)
                        return "\\c" + (char)('A' + c - 1);
                    else if (0 <= c && c <= 0xFF)
                        return string.Format("\\x{0:x2}", c);
                    else if (0 <= c && c <= 0xFFFF)
                        return string.Format("\\u{0:x4}", c);
                    else
                        return string.Format("\\U{0:x8}", c);
            }
        }

        public static string Format(this char c, char delimiter)
        {
            if (!IsQuoteDelimiter(delimiter))
                throw new ArgumentException(SR.DelimiterMustBeQuote, nameof(delimiter));
            return Format((int)c, delimiter);
        }

        public static string Format(this int c, char delimiter)
        {
            if (c < MinChar || c > MaxChar)
                throw new ArgumentOutOfRangeException(nameof(c), SR.InvalidUnicodeCodepoint);
            if (!IsQuoteDelimiter(delimiter))
                throw new ArgumentException(SR.DelimiterMustBeQuote, nameof(delimiter));
            return string.Format("{0}{1}{0}", delimiter, c.Escape(delimiter));
        }

        public static string AsSingleQuote(this char c)
        {
            return AsSingleQuote((int)c);
        }

        public static string AsSingleQuote(this int c)
        {
            if (c < MinChar || c > MaxChar)
                throw new ArgumentOutOfRangeException(nameof(c), SR.InvalidUnicodeCodepoint);
            return c.Format('\'');
        }

        public static string AsDoubleQuote(this char c)
        {
            return AsDoubleQuote((int)c);
        }

        public static string AsDoubleQuote(this int c)
        {
            if (c < MinChar || c > MaxChar)
                throw new ArgumentOutOfRangeException(nameof(c), SR.InvalidUnicodeCodepoint);
            return c.Format('"');
        }

        public static string AsBackQuote(this char c)
        {
            return AsBackQuote((int)c);
        }

        public static string AsBackQuote(this int c)
        {
            if (c < MinChar || c > MaxChar)
                throw new ArgumentOutOfRangeException(nameof(c), SR.InvalidUnicodeCodepoint);
            return c.Format('`');
        }
    }
}
